package com.ssm.platform.service.security;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.ssm.platform.mapper.GroupAuthoritiesMapper;
import com.ssm.platform.mapper.GroupMembersMapper;
import com.ssm.platform.mapper.GroupsMapper;
import com.ssm.platform.mapper.UsersMapper;
import com.ssm.platform.pojo.domain.GroupAuthoritiesDo;
import com.ssm.platform.pojo.domain.GroupMembersDo;
import com.ssm.platform.pojo.domain.GroupsDo;
import com.ssm.platform.pojo.domain.UsersDo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.mapping.Attributes2GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper;
import org.springframework.security.core.userdetails.*;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.ssm.basis.service.constant.SecurityConstant.ANONYMOUS_USERNAME;

/**
 * 用户名密码验证
 *
 * @see UsernamePasswordAuthenticationFilter
 * @see JdbcUserDetailsManager
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService, UserDetailsPasswordService {
    final static Attributes2GrantedAuthoritiesMapper a2gaMapper = new SimpleAttributes2GrantedAuthoritiesMapper();
    final UsersMapper usersMapper;
    final GroupMembersMapper groupMembersMapper;
    final GroupsMapper groupsMapper;
    final GroupAuthoritiesMapper groupAuthoritiesMapper;
    final PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) {
        UsersDo usersDo = usersMapper.selectOne(Wrappers.<UsersDo>lambdaQuery()
                .eq(UsersDo::getUsername, username)
        );
        if (Objects.isNull(usersDo)) {
            throw new UsernameNotFoundException("用户：" + username + " 不存在");
        }

        return new User(username, usersDo.getPassword(), usersDo.getEnabled(),
                !usersDo.getAccountNonExpired(), !usersDo.getCredentialsNonExpired(), !usersDo.getAccountNonLocked(),
                obtainGrantedAuthorities(username)
        );
    }

    /**
     * 获得登录者所有角色的权限集合.
     */
    private Collection<? extends GrantedAuthority> obtainGrantedAuthorities(String username) {
        // 获取用户所在组
        Set<Long> groupIds = groupMembersMapper.selectList(Wrappers.<GroupMembersDo>lambdaQuery()
                .eq(GroupMembersDo::getUsername, username)
        ).stream().map(GroupMembersDo::getGroupId).collect(Collectors.toSet());
        // 获取所有下级组
        groupIds = groupsMapper.listByIds(groupIds).stream().map(GroupsDo::getId).collect(Collectors.toSet());
        // 获取组权限
        Set<String> authoritys = groupAuthoritiesMapper.selectList(Wrappers.<GroupAuthoritiesDo>lambdaQuery()
                .in(GroupAuthoritiesDo::getGroupId, groupIds)
        ).stream().map(GroupAuthoritiesDo::getAuthority).collect(Collectors.toSet());
        authoritys.add(ANONYMOUS_USERNAME);
        return a2gaMapper.getGrantedAuthorities(authoritys);

        // return authoritys.stream().map(role -> {
        //     if (StringUtils.hasLength(role) && role.startsWith(ROLE_PREFIX)) {
        //         return new SimpleGrantedAuthority(role);
        //     }
        //     return new SimpleGrantedAuthority("ROLE_" + role);
        // }).collect(Collectors.toSet());

        // return AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", roles));
    }

    @Override
    public UserDetails updatePassword(UserDetails user, String newPassword) {
        UsersDo usersDo = new UsersDo().setUsername(user.getUsername()).setPassword(passwordEncoder.encode(newPassword));
        if (SqlHelper.retBool(usersMapper.updateById(usersDo))) {
            return user;
        }
        return null;
    }
}
